/*!
* @file Vector3d.cpp
* @brief 三维矢量（点）
* @date 2024/9/11
*/

#include "Vector3D.h"

using namespace data;

template<class float_type>
Vector3<float_type> Vector3<float_type>::operator+(const Vector3<float_type>& v) const
{
    Vector3<float_type> cVctRes;
    cVctRes.x = x + v.x;
    cVctRes.y = y + v.y;
    cVctRes.z = z + v.z;
    return cVctRes;
}

template<class float_type>
Vector3<float_type> Vector3<float_type>::operator&(const Vector3<float_type>& v) const
{
    Vector3<float_type> cVctRes;
    cVctRes.x = x * fabs(v.x);
    cVctRes.y = y * fabs(v.y);
    cVctRes.z = z * fabs(v.z);
    return cVctRes;
}

template<class float_type>
Vector3<float_type> Vector3<float_type>::operator-(const Vector3<float_type>& v) const
{
    Vector3<float_type> cVctRes;
    cVctRes.x = x - v.x;
    cVctRes.y = y - v.y;
    cVctRes.z = z - v.z;
    return cVctRes;
}

template<class float_type>
Vector3<float_type> Vector3<float_type>::operator-() const
{
    return Vector3<float_type>(-x, -y, -z);
}

template<class float_type>
Vector3<float_type>& Vector3<float_type>::operator+=(const Vector3<float_type>& v)
{
    x += v.x;
    y += v.y;
    z += v.z;
    return *this;
}

template<class float_type>
Vector3<float_type>& Vector3<float_type>::operator-=(const Vector3<float_type>& v)
{
    x -= v.x;
    y -= v.y;
    z -= v.z;
    return *this;
}

template<class float_type>
Vector3<float_type> Vector3<float_type>::operator*(float_type scale) const
{
    return Vector3<float_type>(this->x * scale, this->y * scale, this->z * scale);
}

template<class float_type>
Vector3<float_type> Vector3<float_type>::operator/(float_type div) const
{
    return Vector3<float_type>(this->x / div, this->y / div, this->z / div);
}

template<class float_type>
Vector3<float_type>& Vector3<float_type>::operator*=(float_type scale)
{
    x *= scale;
    y *= scale;
    z *= scale;
    return *this;
}

template<class float_type>
Vector3<float_type>& Vector3<float_type>::operator/=(float_type div)
{
    x /= div;
    y /= div;
    z /= div;
    return *this;
}

/// 点积
template<class float_type>
float_type Vector3<float_type>::operator*(const Vector3<float_type>& v) const
{
    return (x * v.x) + (y * v.y) + (z * v.z);
}

template<class float_type>
float_type Vector3<float_type>::Dot(const Vector3<float_type>& v) const
{
    return (x * v.x) + (y * v.y) + (z * v.z);
}

template<class float_type>
Vector3<float_type> Vector3<float_type>::operator%(const Vector3<float_type>& v) const
{
    Vector3<float_type> cVctRes;
    cVctRes.x = (y * v.z) - (z * v.y);
    cVctRes.y = (z * v.x) - (x * v.z);
    cVctRes.z = (x * v.y) - (y * v.x);
    return cVctRes;
}

template<class float_type>
Vector3<float_type> Vector3<float_type>::Cross(const Vector3<float_type>& v) const
{
    Vector3<float_type> cVctRes;
    cVctRes.x = (y * v.z) - (z * v.y);
    cVctRes.y = (z * v.x) - (x * v.z);
    cVctRes.z = (x * v.y) - (y * v.x);
    return cVctRes;
}

template<class float_type>
bool Vector3<float_type>::operator==(const Vector3<float_type>& v) const
{
    return (std::fabs(x - v.x) <= traits_type::epsilon())
        && (std::fabs(y - v.y) <= traits_type::epsilon())
        && (std::fabs(z - v.z) <= traits_type::epsilon());
}

template<class float_type>
bool Vector3<float_type>::operator!=(const Vector3<float_type>& v) const
{
    return !((*this) == v);
}

template<class float_type>
Vector3<float_type>& Vector3<float_type>::ScaleX(float_type f)
{
    x *= f;
    return *this;
}

template<class float_type>
Vector3<float_type>& Vector3<float_type>::ScaleY(float_type f)
{
    y *= f;
    return *this;
}

template<class float_type>
Vector3<float_type>& Vector3<float_type>::ScaleZ(float_type f)
{
    z *= f;
    return *this;
}

template<class float_type>
Vector3<float_type>& Vector3<float_type>::Scale(float_type fX, float_type fY, float_type fZ)
{
    x *= fX;
    y *= fY;
    z *= fZ;
    return *this;
}

template<class float_type>
Vector3<float_type>& Vector3<float_type>::MoveX(float_type f)
{
    x += f;
    return *this;
}

template<class float_type>
Vector3<float_type>& Vector3<float_type>::MoveY(float_type f)
{
    y += f;
    return *this;
}

template<class float_type>
Vector3<float_type>& Vector3<float_type>::MoveZ(float_type f)
{
    z += f;
    return *this;
}

template<class float_type>
Vector3<float_type>& Vector3<float_type>::Move(float_type fX, float_type fY, float_type fZ)
{
    x += fX;
    y += fY;
    z += fZ;
    return *this;
}

template<class float_type>
Vector3<float_type>& Vector3<float_type>::RotateX(float_type f)
{
    Vector3<float_type> cPt(*this);

    float_type fsin = sin(f);
    float_type fcos = cos(f);
    y = (cPt.y * fcos) - (cPt.z * fsin);
    z = (cPt.y * fsin) + (cPt.z * fcos);
    return *this;
}

template<class float_type>
Vector3<float_type>& Vector3<float_type>::RotateY(float_type f)
{
    Vector3<float_type> cPt(*this);

    float_type fsin = sin(f);
    float_type fcos = cos(f);
    x = (cPt.z * fsin) + (cPt.x * fcos);
    z = (cPt.z * fcos) - (cPt.x * fsin);
    return *this;
}

template<class float_type>
Vector3<float_type>& Vector3<float_type>::RotateZ(float_type f)
{
    Vector3<float_type> cPt(*this);

    float_type fsin = sin(f);
    float_type fcos = cos(f);
    x = (cPt.x * fcos) - (cPt.y * fsin);
    y = (cPt.x * fsin) + (cPt.y * fcos);
    return *this;
}

template<class float_type>
Vector3<float_type>& Vector3<float_type>::Set(float_type fX, float_type fY, float_type fZ)
{
    x = fX;
    y = fY;
    z = fZ;
    return *this;
}

template<class float_type>
Vector2<float_type> Vector3<float_type>::Vec2() const
{
    return Vector2<float_type>(x, y);
}

template<class float_type>
float_type Vector3<float_type>::Length() const
{
    return std::sqrt((x * x) + (y * y) + (z * z));
}

template<class float_type>
float_type Vector3<float_type>::Sqr() const
{
    return ((x * x) + (y * y) + (z * z));
}

template<class float_type>
Vector3<float_type>& Vector3<float_type>::Normalize()
{
    float_type fLen = Length();
    if (fLen != 0.0 && fLen != 1.0) {
        x /= fLen;
        y /= fLen;
        z /= fLen;
    }
    return *this;
}

template<class float_type>
Vector3<float_type> Vector3<float_type>::Normalized() const
{
    Vector3<float_type> vec = (*this);
    vec.Normalize();
    return vec;
}

template<class float_type>
float_type Vector3<float_type>::GetAngle(const Vector3<float_type>& v) const
{
    float_type len1 = Length();
    float_type len2 = v.Length();
    if (len1 <= traits_type::epsilon() || len2 <= traits_type::epsilon()) {
        return traits_type::quiet_NaN();  // division by zero
    }

    float_type dot = Dot(v);
    dot /= len1;
    dot /= len2;

    if (dot <= -1.0) {
        return (float_type)D_PI;
    }
    if (dot >= 1.0) {
        return 0.0;
    }

    return float_type(acos(dot));
}

template<class float_type>
float_type Vector3<float_type>::Distance(const Vector3<float_type>& p) const
{
    float_type dx = (x - p.x);
    float_type dy = (y - p.y);
    float_type dz = (z - p.z);
    return sqrt(dx * dx + dy * dy + dz * dz);
}

template<class float_type>
bool Vector3<float_type>::IsZero() const
{
    return x == 0.0 && y == 0.0 && z == 0.0;
}

template<class float_type>
bool Vector3<float_type>::IsEqual(const Vector3<float_type>& p, float_type tol) const
{
    return Distance(p) <= tol;
}

template<class float_type>
bool Vector3<float_type>::IsParallel(const Vector3<float_type>& v, float_type tol) const
{
    float_type angle = GetAngle(v);
    if (std::isnan(angle))
    {
        return false;
    }

    return angle <= tol || D_PI - angle <= tol;
}

template<class float_type>
bool Vector3<float_type>::IsNormal(const Vector3<float_type>& v, float_type tol) const
{
    float_type angle = GetAngle(v);
    if (std::isnan(angle))
    {
        return false;
    }

    float_type diff = std::abs(D_PI / 2.0 - angle);
    return diff <= tol;
}

template<class float_type>
Vector3<float_type> Vector3<float_type>::ProjectToLine(const Vector3<float_type>& base, const Vector3<float_type>& dir) const
{
    float_type t = ((*this - base) * dir) / (dir * dir);
    return base + dir * t;
}

template<class float_type>
float_type Vector3<float_type>::DistanceToLine(const Vector3<float_type>& base, const Vector3<float_type>& dir) const
{
    // clang-format off
    return std::fabs((dir % (*this - base)).Length() / dir.Length());
    // clang-format on
}

template<class float_type>
Vector3<float_type> Vector3<float_type>::ProjectToPlane(const Vector3<float_type>& base, const Vector3<float_type>& normal)
{
    Vector3<float_type> temp(normal);
    return *this - (temp *= ((*this - base) * temp) / temp.Sqr());
}

template<class float_type>
float_type Vector3<float_type>::DistanceToPlane(const Vector3<float_type>& base, const Vector3<float_type>& normal) const
{
    return ((*this - base) * normal) / normal.Length();
}

//导出
namespace Base
{
    template class DATA_API Vector3<float>;
    template class DATA_API Vector3<double>;
}
